﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

namespace Microsoft.WowAddonStudio.FrameXml.Serialization
{
    [Serializable]
    [DesignerCategory("code")]
	public class ScriptContainer : SerializationObject, IXmlSerializable, IEnumerable
    {
    	private readonly Dictionary<EventName, Script> scripts = new Dictionary<EventName, Script>();

		/// <summary>
		/// Gets or sets the <see cref="Microsoft.WowAddonStudio.FrameXml.Serialization.Script"/> 
		/// with the specified event name.
		/// </summary>
		/// <value></value>
		public Script this[EventName eventName]
		{
			get
			{
				if (scripts.ContainsKey(eventName))
				 return scripts[eventName];

				return null;
			}
			set { scripts[eventName] = value; }
		}

    	/// <summary>
		/// Adds the specified event name.
		/// </summary>
		/// <param name="eventName">Name of the event.</param>
		/// <param name="script">The script.</param>
		public void Add(EventName eventName, Script script)
		{
			this[eventName] = script;
		}


    	/// <summary>
		/// Adds the specified event name.
		/// </summary>
		/// <param name="eventName">Name of the event.</param>
		/// <param name="script">The script.</param>
		public void Add(EventName eventName, string script)
		{
			this[eventName] = new Script(eventName) { ScriptBlock = script };
		}

		/// <summary>
		/// Removes the specified event name.
		/// </summary>
		/// <param name="eventName">Name of the event.</param>
		public void Remove(EventName eventName)
		{
			scripts.Remove(eventName);
		}

		/// <summary>
		/// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </summary>
		/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only. </exception>
		public void Clear()
		{
			scripts.Clear();
		}

    	/// <summary>
		/// Gets the count.
		/// </summary>
		/// <value>The count.</value>
    	public int Count
    	{
    		get { return scripts.Count; }
    	}

    	public bool IsReadOnly
    	{
    		get { throw new NotImplementedException(); }
    	}

    	/// <summary>
		/// Determines whether [contains] [the specified event name].
		/// </summary>
		/// <param name="eventName">Name of the event.</param>
		/// <returns>
		/// 	<c>true</c> if [contains] [the specified event name]; otherwise, <c>false</c>.
		/// </returns>
		public bool Contains(EventName eventName)
		{
			return scripts.ContainsKey(eventName);
		}

    	#region IXmlSerializable Members

		/// <summary>
		/// This method is reserved and should not be used. When implementing the IXmlSerializable interface, you should return null (Nothing in Visual Basic) from this method, and instead, if specifying a custom schema is required, apply the <see cref="T:System.Xml.Serialization.XmlSchemaProviderAttribute"/> to the class.
		/// </summary>
		/// <returns>
		/// An <see cref="T:System.Xml.Schema.XmlSchema"/> that describes the XML representation of the object that is produced by the <see cref="M:System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter)"/> method and consumed by the <see cref="M:System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader)"/> method.
		/// </returns>
    	public XmlSchema GetSchema()
    	{
    		return null;
    	}

		/// <summary>
		/// Generates an object from its XML representation.
		/// </summary>
		/// <param name="reader">The <see cref="T:System.Xml.XmlReader"/> stream from which the object is deserialized.</param>
    	public void ReadXml(XmlReader reader)
    	{
			string name = reader.Name;
			if (reader.NodeType == XmlNodeType.EndElement) return;

			// ScriptContainer is part of Frame, we accept any name that XmlSerializer is mapped to
			reader.ReadStartElement(name);

			// Iterate through the child nodes while our node is not closed
			while (reader.NodeType != XmlNodeType.EndElement)
			{
				// Check whether node is an XML element
				if (reader.NodeType == XmlNodeType.Element)
				{
					// Make sure the event name is defined in the EventName enum
					if (Enum.IsDefined(typeof(EventName), reader.Name))
					{
						// Parse the event name
						var eventName = (EventName)Enum.Parse(typeof (EventName), reader.Name);

						// Create a Script instance for the event
						var script = new Script(eventName);

						// Read the script
						script.ReadXml(reader);

						// Set the script for the event
						this[eventName] = script;
					}
					else
						reader.Read();
				}
				else
					reader.Read();
			}
			//If Scripts block doesn't contains any element then
			//don't read the next element because it is related to another node.
			if (reader.NodeType == XmlNodeType.EndElement && reader.Name == name)
			{
				// Read our end element
				reader.ReadEndElement();
			}
    	}

		/// <summary>
		/// Converts an object into its XML representation.
		/// </summary>
		/// <param name="writer">The <see cref="T:System.Xml.XmlWriter"/> stream to which the object is serialized.</param>
    	public void WriteXml(XmlWriter writer)
    	{
			// Iterate through the scripts and write them
			foreach (var script in scripts.Values)
                script.WriteXml(writer);
    	}

    	#endregion


		/// <summary>
		/// Returns an enumerator that iterates through a collection.
		/// </summary>
		/// <returns>
		/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
		/// </returns>
    	public IEnumerator GetEnumerator()
    	{
    		return scripts.GetEnumerator();
    	}

		/// <summary>
		/// Gets the scripts.
		/// </summary>
		/// <value>The scripts.</value>
    	public IEnumerable<Script> Scripts
    	{
    		get
    		{
    			return scripts.Values;
    		}
    	}

    }
}